package ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.impl;

import ace.cmp.converter.api.AceConverter;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.DtoManager;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;

/**
 * 传输对象基础业务实现.
 *
 * @author caspar
 */
@Slf4j
public abstract class AbstractDtoManager<EntityT, IdT, EntityDtoT>
    extends AbstractManager<EntityT, IdT>
    implements DtoManager<EntityT, IdT, EntityDtoT> {

  protected abstract AceConverter<EntityT, EntityDtoT> getEntityToDtoConverter();

  protected abstract AceConverter<EntityDtoT, EntityT> getEntityDtoToEntityConverter();


  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT saveDto(EntityDtoT entityDto) {
    return this.saveDto(entityDto, true);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT saveDto(EntityDtoT entityDto, boolean saveNullFieldFlag) {
    EntityT entity = this.convertToEntity(entityDto);
    return this.save(entity, saveNullFieldFlag);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT saveDtoIgnore(EntityDtoT entityDto, boolean saveNullFieldFlag) {
    EntityT entity = this.convertToEntity(entityDto);
    return this.saveIgnore(entity, saveNullFieldFlag);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT addDto(EntityDtoT entityDto) {
    return this.addDto(entityDto, true);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT addDto(EntityDtoT entityDto, Boolean saveNullFieldFlag) {
    EntityT entity = this.convertToEntity(entityDto);
    return this.addEntity(entity, saveNullFieldFlag, false);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT addDtoIgnore(EntityDtoT entityDto, boolean saveNullFieldFlag) {
    EntityT entity = this.convertToEntity(entityDto);
    return this.addIgnore(entity, saveNullFieldFlag);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT modifyDto(EntityDtoT entityDto) {
    return this.modifyDto(entityDto, true);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT modifyDto(EntityDtoT entityDto, boolean saveNullFieldFlag) {
    EntityT entity = this.convertToEntity(entityDto);
    return this.modify(entity, saveNullFieldFlag);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public IdT modifyDtoIgnore(EntityDtoT entityDto, boolean saveNullFieldFlag) {
    EntityT entity = this.convertToEntity(entityDto);
    return this.modifyIgnore(entity, saveNullFieldFlag);
  }

  @Override
  public EntityDtoT getDtoById(IdT id) {
    EntityT entity = this.getEntityById(id);
    EntityDtoT entityDto = this.getEntityToDtoConverter().convert(entity);
    return entityDto;
  }

  @Override
  public List<EntityDtoT> getDtoListByIdIn(List<IdT> ids) {
    List<EntityT> entities = this.getEntityListByIdIn(ids);
    List<EntityDtoT> entityDtoList = this.convertToEntityDto(entities);
    return entityDtoList;
  }

  @Override
  public Map<IdT, EntityDtoT> getDtoMapByIdIn(List<IdT> ids) {
    Map<IdT, EntityT> entityMap = this.getEntityMapByIdIn(ids);
    Map<IdT, EntityDtoT> entityDtoMap = this.entityMapToEntityDtoMapConvert(entityMap);
    return entityDtoMap;
  }

  @Override
  public Page<EntityDtoT> searchDto(String query, Pageable pageable) {
    Page<EntityT> entityPage = this.searchEntity(query, pageable);
    Page<EntityDtoT> entityDtoPage = entityPage.map(entity -> this.getEntityToDtoConverter().convert(entity));
    return entityDtoPage;
  }

  @Override
  public EntityT convertToEntity(EntityDtoT entityDto) {
    return this.getEntityDtoToEntityConverter().convert(entityDto);
  }

  @Override
  public List<EntityT> convertToEntity(List<EntityDtoT> entities) {
    return entities
        .parallelStream()
        .map(entity -> this.getEntityDtoToEntityConverter().convert(entity))
        .collect(Collectors.toList());
  }

  @Override
  public EntityDtoT convertToEntityDto(EntityT entity) {
    return this.getEntityToDtoConverter().convert(entity);
  }

  @Override
  public List<EntityDtoT> convertToEntityDto(List<EntityT> entities) {
    return entities
        .parallelStream()
        .map(entity -> this.getEntityToDtoConverter().convert(entity))
        .collect(Collectors.toList());
  }

  protected Map<IdT, EntityDtoT> entityMapToEntityDtoMapConvert(Map<IdT, EntityT> entityMap) {
    return entityMap
        .entrySet()
        .parallelStream()
        .map(
            entityEntry ->
                new AbstractMap.SimpleEntry<>(
                    entityEntry.getKey(),
                    this.getEntityToDtoConverter().convert(entityEntry.getValue())
                )
        )
        .collect(
            HashMap::new,
            (m, v) -> {
              m.put(v.getKey(), v.getValue());
            },
            HashMap::putAll);
  }
}
